package org.pictual.business.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ResourceBundle;

import javax.naming.NamingException;

import org.apache.log4j.Logger;

public class AccesoBD{


	/**
	 * Logger
	 */
	private static final Logger log = Logger.getLogger(AccesoBD.class);

	// Propiedades

	private final static long TIEMPO_MAX_SQL = 10;
	private static ResourceBundle dbParams=ResourceBundle.getBundle("org.pictual.business.dao.AccesoBD");
	private Connection connection;
	public static final String DATASOURCE_NAME = dbParams.getString("dataSourceName");

	public AccesoBD(){
	}
	
	public void conectar() throws SQLException, NamingException{
		log.info("Datasource a cargar: "+DATASOURCE_NAME);
		try{
			connection = ServiceLocator.getInstance().getDatasource(DATASOURCE_NAME).getConnection();
		}catch(Exception e){
			log.error("NO CONECTA: "+e.getMessage(), e);
		}
	}
	
	public boolean conecta() throws SQLException, NamingException{
		log.info("Datasource a cargar: "+DATASOURCE_NAME);
		try{
			connection = ServiceLocator.getInstance().getDatasource(DATASOURCE_NAME).getConnection();
		}catch(Exception e){
			log.error("NO CONECTA: "+e.getMessage(), e);
			return false;
		}
		desconectar();
		return true;
	}
	

	public void desconectar(){
		try {
			if(connection!=null){
				connection.close();
				connection=null;
				 log.info("Se cierra la conexion");
			}
		} catch (SQLException e) {
			log.error("Error al cerrar la conexion", e);
		}
	}


	/**
	 * Ejecuta la consulta a partir de un String y una lista de parámetros que
	 * deben coincidir en número con los <code>?</code> de la consulta.
	 * 
	 * @param queryConsulta
	 *            consulta SQL para realizar una precompilacion
	 * 
	 * @param parametros
	 *            Lista de parametros en el orden en el que deben ser
	 *            establecidos en la consulta preparada. Si no se desean pasar
	 *            parámetros, se puede pasar <code>null</code>.
	 * 
	 * @throws SQLException
	 *             se ha producido un error accediendo a la base de datos
	 * 
	 * @return conjunto de resultados
	 * 
	 */
	public ResultSet ejecutarConsulta(String query, Object... parametros) throws SQLException
	{
		long t1 = System.currentTimeMillis();
		log.debug(query);
		
    	log.info("Ejecutando consulta: '" + query + "'");

        try {
            if(connection==null)conectar();
        } catch (Exception e) {
            log.error("Error al conectar: ", e);
            return null;
        }

        if (parametros != null) {
            for( Object object : parametros) {
                log.info("Parámetro: " + object);
            }
        }
		
		
		ResultSet rs = null;
		PreparedStatement pstmt = null;
		try {
			if (connection != null) {
				pstmt = connection.prepareStatement(query);
				if (parametros != null) {
					int i= 0;
					for(Object parametro: parametros) {
						if (parametro != null) {
							log.info("Parámetro " + i + "= " + parametro);
							pstmt.setObject(i + 1, parametro);
						} else {
							log.info("Parámetro " + i + "= nulo");
							pstmt.setObject(i + 1, parametro, Types.VARCHAR);
						}
						i++;
					}
				}
			}
		} catch (SQLException sqle) {
			log.error("Error preparando consulta con excepcion SQL: " + sqle);
		} catch (Exception e) {
			log.error("Error preparando consulta con excepcion: " + e);
		} finally {
			try{
				rs = pstmt.executeQuery();
			} catch(Exception e){
				log.error("ERROR EN RESULSET");
			}
        }
//		pstmt.close();
		long t2 = System.currentTimeMillis();
		long t = (t2 - t1)/1000; // en segundos
		if(t >= TIEMPO_MAX_SQL) {
			log.warn("LA CONSULTA TARDA " + t + " SEGUNDOS !!!!!");
			log.warn("LA CONSULTA ES: " + query);
			log.warn("LOS PARAMETROS SON: ");
			if (parametros != null) {
				for( Object object : parametros) {
					log.warn("Parámetro: " + object);
				}
			}
		}else {
			log.info("LA CONSULTA TARDA " + t + " SEGUNDOS");
		}

		return rs;
	}
	
	/**
     * Ejecuta una actualización a partir de un String con la consulta y una
     * lista de parámetros que deben coincidir en número con los <code>?</code>
     * de la consulta.
     * 
     * @param query
     *            consulta SQL para realizar una precompilacion
     * 
     * @param parametros
     *            Lista de parametros en el orden en el que deben ser
     *            establecidos en la consulta preparada. Si no se desean pasar
     *            parámetros, se puede pasar <code>null</code>.
     * 
     * @throws SQLException
     *             se ha producido un error accediendo a la base de datos
     * 
     * @return Número de filas afectadas por la actualización.
     */
    public int ejecutarActualizacion( String query, Object... parametros) throws SQLException
    {
    	long t1 = System.currentTimeMillis();
        log.debug("AccesoBD.ejecutarActualizacion: inicio ");
        log.debug(query);
        log.debug(parametros);

        PreparedStatement pstmt = null;
        int numRegistrosModificados = -1;

        try {
        	if(connection==null)
        		this.conectar();
        	
            if (connection != null) {
                pstmt = (PreparedStatement) connection.prepareStatement(query);
                if (parametros != null) {
                	int i= 0;
                    for(Object parametro: parametros) {
                        if (parametro != null) {
                            log.info("Parámetro_ " + (i + 1) + "= " + parametro);
                            pstmt.setObject(i + 1, parametro);
                        } else {
                            log.info("Parámetro_ " + (i + 1) + "= null");
                            pstmt.setObject(i + 1, parametro, Types.VARCHAR);
                        }
                        i++;
                    }
                }
            }
            log.info("AccesoBD.ejecutarActualizacion: antes de pstmt.executeUpdate");
            numRegistrosModificados = pstmt.executeUpdate();
        }catch (SQLException e) {
        	throw e;
        }catch (Exception e) {
            log.error("AccesoBD.ejecutarActualizacion: Error pstmt.executeUpdate: " + e + "\n");
        } finally {
            try {
                pstmt.close();
            } catch (Exception e) {
                log.error("AccesoBD.ejecutarActualizacion: Error pstmt.close: " + e);
            }
        }

        log.debug("AccesoBD.ejecutarActualizacion: fin. regModificados=" + numRegistrosModificados);
        long t2 = System.currentTimeMillis();
    	long t = (t2 - t1)/1000; // en segundos
    	if(t >= TIEMPO_MAX_SQL) {
    		log.warn("LA CONSULTA TARDA " + t + " SEGUNDOS !!!!!");
    		log.warn("LA CONSULTA ES: " + query);
    		log.warn("LOS PARAMETROS SON: ");
    		if (parametros != null) {
                for( Object object : parametros) {
                    log.warn("Parámetro: " + object);
                }
            }
    	}else {
    		log.info("LA CONSULTA TARDA " + t + " SEGUNDOS");
   		}
        return numRegistrosModificados;
    }
	/**
	 * Confirma todos los cambios llevados a cabo en la base de datos desde el
	 * último commit o rollback
	 * 
	 * @throws SQLException
	 *             se ha producido un error accediendo a la base de datos
	 */
	public void commit( ) throws SQLException
	{
		connection.commit();
	}

	/**
	 * Deshace todos los cambios llevados a cabo en la base de datos desde el
	 * último commit o rollback
	 * 
	 * @throws SQLException
	 *             se ha producido un error accediendo a la base de datos
	 */
	public void rollback( ) throws SQLException
	{
		connection.rollback();
	}

	/**
	 * Deshace todos los cambios llevados a cabo en la base de datos desde el
	 * último commit o rollback
	 * 
	 */
	public void rollbackSinExcepcion( )
	{
		if( connection == null) {
			log.warn( "AccesoBD.rollbackSinExcepcion: sin conexión establecida");
			return;
		}

		try {
			connection.rollback();
		} catch (SQLException e) {
			log.error( "AccesoBD.rollbackSinExcepcion: Error al hacer rollback", e);
		}
	}

	/**
	 * Obtiene el número de filas del ResultSet que se le pasa como argumento.
	 * Este método sólo funciona con ResultSets de tipo SCROLL_XXX, ya que con
	 * los de tipo FORWARD_ONLY produce una SQLException
	 * 
	 * @param rs
	 *            objeto ResultSet del que se quiere averiguar su número de
	 *            filas
	 * 
	 * @throws SQLException
	 *             se ha producido un error accediendo a la base de datos
	 * 
	 * @return número de filas del ResultSet
	 */
	public int getNumeroFilas(ResultSet rs) throws SQLException
	{
		int numFilas = 0;

		rs.beforeFirst();
		while (rs.next()) {
			numFilas++;
		}
		rs.beforeFirst();

		return numFilas;
	}

	public void cerrarResultSet( ResultSet rs){
        if (rs != null) {
            try {
                Statement stmt = rs.getStatement();
                rs.getStatement().close();
                if (stmt != null) {
                    stmt.close();
                }
                log.info("Se cierra el resulset");
            } catch (SQLException e) {
                log.error("Error buscando y cerrando Statement: ", e);
            } finally {
                try {
                    rs.close();
                } catch (SQLException e) {
                    log.error("Error cerrando ResultSet", e);
                }
            }
        }
    }
}